package com.whately.core.base.config;

import com.whately.core.base.annotation.IgnoreSession;
import com.whately.core.base.annotation.InnerApi;
import com.whately.core.base.annotation.InnerController;
import com.whately.core.base.config.intf.FrameServiceCallerBackIntf;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;

@Component
public class SpringConfigurer implements ApplicationListener<ContextRefreshedEvent> {

    private final static Logger logger = LoggerFactory.getLogger(SpringConfigurer.class);


    private Map<String, ServiceMethod> serviceMap = new HashMap<>();


    private static ApplicationContext applicationContext;


    /**
     * Async注解使用时
     * org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor:242
     * -More than one TaskExecutor bean found within the context, and none is named 'taskExecutor'.
     * Mark one of them as primary or name it 'taskExecutor' (possibly as an alias) in order to use it for async processing: [threadPoolTaskExecutor, taskScheduler]
     * @return
     */
    @Bean(name = {"threadPoolTaskExecutor","taskExecutor"})
    @Primary
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor =  new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        // 线程池维护线程的最大数量
        executor.setMaxPoolSize(1000);
        return executor;
    }


    public Map<String, ServiceMethod> unModifiableServiceMap(){
        return  Collections.unmodifiableMap(serviceMap);
    }

    protected Map<String, ServiceMethod> getServiceMap(){
        return serviceMap;
    }



    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //获取全局上下文
        if(applicationContext == null){
            applicationContext = event.getApplicationContext();
        }

        //查找bean
        logger.info("start to register service info");

        final Map<String /* bean name*/,Object /*bean instance*/> serviceBeanMap = new HashMap<>();
        serviceBeanMap.putAll( applicationContext.getBeansWithAnnotation(Controller.class));
        serviceBeanMap.putAll( applicationContext.getBeansWithAnnotation(RestController.class));
        serviceBeanMap.putAll( applicationContext.getBeansWithAnnotation(InnerController.class));


        register(applicationContext, serviceBeanMap);


    }



    private void register(ApplicationContext applicationContext, Map<String, Object> serviceBeanMap) {
        if(MapUtils.isEmpty(serviceBeanMap)) {
            return;
        }

        Map<String, FrameServiceCallerBackIntf> callerBackMap =  getApplicationContext().getBeansOfType(FrameServiceCallerBackIntf.class);

        final String tag = "method=";

        for (Object bean : serviceBeanMap.values()) {
            //内部controller
            boolean innerControllerFlag = bean.getClass().isAnnotationPresent(InnerController.class);

            // 获取每一个方法上的request mapping的value，可能为空
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
            //logger.info("Register all ufo service for controller:{} begin", bean);
            for (Method method : methods) {
                InnerApi innerApiAnnotation = method.getAnnotation(InnerApi.class);
                IgnoreSession ignoreSessionAnnotation = method.getAnnotation(IgnoreSession.class);
                boolean innerMethodApiFlag = innerApiAnnotation==null?false:true;
                boolean ignoreSessionApiFlag = ignoreSessionAnnotation==null?false:true;

                String[] params;

                //取RequestMapping
                RequestMapping method_requestMappingAnno = method.getAnnotation(RequestMapping.class);
                params = method_requestMappingAnno==null?null:method_requestMappingAnno.params();

                //取postMapping
                if(params==null){
                    PostMapping post_method_requestMappingAnno = method.getAnnotation(PostMapping.class);
                    params = post_method_requestMappingAnno==null?null:post_method_requestMappingAnno.params();

                    //取getMapping
                    if(params==null){
                        GetMapping get_method_requestMappingAnno = method.getAnnotation(GetMapping.class);
                        params = get_method_requestMappingAnno==null?null:get_method_requestMappingAnno.params();
                    }
                }



                if (params != null && params.length == 1 && params[0].trim().startsWith(tag)) {
                    String methodStr = params[0].trim().substring(tag.length());
                    if (StringUtils.isNotBlank(methodStr)) {
                        boolean inner = innerControllerFlag||innerMethodApiFlag;
                        serviceMap.put(methodStr, new ServiceMethod(bean, method,ignoreSessionApiFlag,inner));

                        if(callerBackMap!=null&&callerBackMap.size()>0&&!ignoreSessionApiFlag&&!inner){
                            for(Map.Entry<String, FrameServiceCallerBackIntf> entry : callerBackMap.entrySet()){
                                entry.getValue().apply(method,methodStr);
                            }
                        }
                    }
                }

            }


        }




    }




    /**
     * 获取上下文
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通过name获取 Bean.
     * @param name
     * @return
     */
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean.
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过class获取Bean.
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> Map<String, T> getBeansOfType(Class<T> clazz){
        return getApplicationContext().getBeansOfType(clazz);
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }


    /**
     * 主动向Spring容器中注册bean
     *
     * @param applicationContext Spring容器
     * @param name               BeanName
     * @param clazz              注册的bean的类性
     * @param args               构造方法的必要参数，顺序和类型要求和clazz中定义的一致
     * @param <T>
     * @return 返回注册到容器中的bean对象
     */
    public static <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clazz,
                                     Object... args) {
        if(applicationContext.containsBean(name)) {
            /*
            Object bean = applicationContext.getBean(name);
            if (bean.getClass().isAssignableFrom(clazz)) {
                return (T) bean;
            } else {
                throw new RuntimeException("BeanName duplicate " + name);
            }

             */
            throw new RuntimeException("register bean name duplicate " + name);
        }


        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        for (Object arg : args) {
            beanDefinitionBuilder.addConstructorArgValue(arg);
        }
        BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();

        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
        beanFactory.registerBeanDefinition(name, beanDefinition);
        return applicationContext.getBean(name, clazz);
    }


}
